package klog

import (
	"gitee.com/klogsdk/klog-go-sdk/internal/apierr"
	"math"
	"math/rand"
	"reflect"
	"strings"
	"time"
)

var BeijingZone = time.FixedZone("beijing", 3600*8)
var timeFormat = "2006-01-02T15:04:05.000+0800"

func makeRandomTimer(count int) *time.Timer {
	var mSec float64
	if count <= 0 {
		count = 1
	}
	if count < 10 {
		mSec = math.Pow(2, float64(count)) * 200
	}

	mSec += rand.Float64() * mSec

	if mSec == 0 || mSec > 90000 {
		mSec = 90000
	}
	return time.NewTimer(time.Duration(mSec) * time.Millisecond)
}

func IsError(err error, code string) bool {
	if e, ok := err.(*apierr.BaseError); ok {
		return e.Code() == code
	}
	return false
}

func nowString() string {
	return time.Now().In(BeijingZone).Format(timeFormat)
}

func sliceContains(ss []string, x string) bool {
	for _, s := range ss {
		if x == s {
			return true
		}
	}
	return false
}

func convertStruct2Map(s interface{}, tagName string) map[string]interface{} {
	res := map[string]interface{}{}
	v := reflect.ValueOf(s)

	// 指针类型取值
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	// 非struct类型不能转换
	if v.Kind() != reflect.Struct {
		return nil
	}

	t := v.Type()
	for i := 0; i < v.NumField(); i++ {
		fi := t.Field(i)

		if fi.Anonymous && fi.IsExported() {
			m := convertStruct2Map(v.Field(i).Interface(), tagName)

			// 可能会覆盖前面的值，由遍历 field 的顺序决定
			for k, vv := range m {
				res[k] = vv
			}
		} else if fi.IsExported() {
			tagValue := fi.Tag.Get(tagName)
			if tagValue != "" {
				if !isEmptyValue(v.Field(i)) && strings.Contains(tagValue, ",omitempty") {
					idx := strings.Index(tagValue, ",omitempty")
					res[tagValue[:idx]] = v.Field(i).Interface()
				}

				if !strings.Contains(tagValue, ",omitempty") {
					res[tagValue] = v.Field(i).Interface()
				}
			}
		}
	}
	return res
}

func getKind(val reflect.Value) reflect.Kind {
	kind := val.Kind()

	switch {
	case kind >= reflect.Int && kind <= reflect.Int64:
		return reflect.Int
	case kind >= reflect.Uint && kind <= reflect.Uint64:
		return reflect.Uint
	case kind >= reflect.Float32 && kind <= reflect.Float64:
		return reflect.Float32
	default:
		return kind
	}
}

func isEmptyValue(v reflect.Value) bool {
	switch getKind(v) {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

func convertStruct2MapDefault(s interface{}) map[string]interface{} {
	return convertStruct2Map(s, "json")
}
